Java Development Kit (JDK) is a software development kit for producing Java programs. JDK version 1.5 introduced several extensions to the Java programming language. One of these extensions is the introduction of “generic types.”
Generic types in Java are similar to, but not exactly the same as, templates in the C++ programming language. Generic types may be most easily understood through a discussion of the scenarios that make the use of generic types beneficial.
According to the syntactical rules of strongly typed programming languages such as Java, the data type of a variable is supposed to be expressed in the declaration of that variable. The following declaration is illustrative:                Integer x=new Integer(0);In the above declaration, the variable “x” is declared to be of type “Integer.” Thus, any attempt to assign, to “x,” an object that not an instance of the “Integer” class or a subtype thereof, should be forbidden and should cause the compiler to alert the programmer that something is amiss in the assignment.        
The following declaration is somewhat more sophisticated and informative:                List myIntList=new LinkedList( );In the above declaration, the variable “myIntList” is declared to be of type “List.” When the above expression is evaluated, a new instance of the “LinkedList” class (i.e., a “LinkedList” object) is created (“instantiated”) via the invocation of the “LinkedList( )” constructor method and assigned to “myIntList.” The “List” and “LinkedList” classes are used as examples in the discussion below, but it should be understood that the behaviors of the classes discussed below are not limited only to these classes.        
Like other variables of type “List,” the variable “myIntList” can be assigned “LinkedList” objects. The “LinkedList” object is a linked list of other objects. Prior to the introduction of generic types in Java, the compiler could not determine what the specific types of these other objects were. The type system was a conservative approximation of run-time behavior. For example, without generic types, if only “Foo”-type objects were “inserted” into a LinkedList, then the compiler could not determine, upon “extraction” of these objects from the LinkedList, that the objects were of type “Foo.” Without generic types, the compiler merely could determine that these objects were instances of some indeterminable subtype of the “Object” class; in Java, all classes are subtypes of the “Object” class. Because this was as specific as the compiler could get, the “extracted” objects could only have the methods of the “Object” class (as opposed to the “Foo” class) invoked relative to them.
Because of this shortcoming, it often became necessary for a programmer to “cast” such a returned object into a particular data type prior to assigning that object to a variable that was declared to be of the particular data type. The following code is demonstrative:                Integer x=(Integer) myIntList.iterator( ).next( );In the above code, the “Object” type object returned by the method is expressly cast into an variable of type “Integer.” If this cast were not performed, then the compiler might complain that a non-“Integer” type object was being assigned to a variable of type “Integer.” Indeed, such a complaint would be beneficial, because if the compiler did not complain about such an assignment, then the compiled program might exhibit unexpected and unwanted behavior if and when the method returned an object that actually was not an Integer.        
Having to cast objects in this manner usually irritates programmers and makes source code undesirably verbose and less readable. Some might even consider casting to be a “dirty” programming practice. Fortunately, the advent of generic types in Java made casting unnecessary, at least for the purposes discussed above. A sample declaration that incorporates generic types follows:                List<Integer>myIntList=new LinkedList<Integer>( );        
This is similar to the non-generically typed declaration above, except that the declaration expressly indicates that the objects within the linked list assigned to “myIntList” are going to be instances of class “Integer.” In light of the declaration, the compiler knows that every object returned by the “myIntList.iterator( ).next( )” method is an “Integer” object. The compiler will not complain about the expression                Integer x=myIntList.iterator( ).next( );because the compiler can determine that the object returned by the method must be an “Integer” object, and that variable “x” can reference objects of type “Integer.” A single declaration makes potentially many casts unnecessary.        
It is also should be noted that generic types allow for specificity in this manner without requiring multiple separate implementations of the class. Although “myIntList” is a “List” object that references “Integer” objects, it was not necessary for the programmer to specifically code an implementation of the “List” class to reference only “Integer” objects. Generic types allow a class to be defined once in a generic manner, and then invoked multiple times in different specific declarations.
Due to the more precise approximation of run-time behavior that generic types provide, generic types also help to avoid runtime failures.
An actual example of generic type declarations is shown below:
public interface List<E> {void add(E x);Iterator<E> iterator( );}public interface Iterator<E> {E next( );boolean hasNext( );}
In the above generic type declarations, the angle bracket-enclosed parameters (e.g., “<E>”) are called the “formal type parameters” of the generic types. The specific invocation of such a generic type is called a “parameterized type.” For example, the following type expression is a parameterized type:                List<Integer>In the foregoing parameterized type, “<Integer>” is an example of an “actual type argument.” Parameterized types specify actual type arguments that are to be put in the place of formal type parameters when the generic type declaration is invoked.        
Although Java generic types and C++ templates are somewhat similar syntactically and functionally, there are some significant differences between the two. The declaration of a Java generic type is not expanded, either within source code or within binary code, into multiple different specific declarations for different actual type arguments. Instead, much like with non-generic type declarations in Java, a Java generic type declaration is compiled into a single class file.
Because generic types are so useful, many of the non-generic type declarations that used to exist in the Java libraries have been replaced with generic type declarations. Non-generic method declarations also have been replaced in these libraries with generic method declarations. In other words, the former non-generic type and method declarations have been “generified.” Programmers who are writing new Java programs can make use of the generic types and methods by putting parameterized types in their programs.
However, because the Java language has already been in existence for a significant period of time, many Java programs already have been written. As can be imagined, many of these programs do not contain parameterized types. Instead, many of these programs contain invocations that do not specify an actual type argument. If the compiler required every invocation of a generic type declaration to be a parameterized type (as opposed to an invocation that did not specify an actual type argument), then many of the existing Java programs would be incompatible with the new Java libraries.
One attempt to solve the above problem would be to require existing Java programs to be modified so that every invocation of a generic type declaration specified an actual type argument for each formal type parameter in the generic type declaration. Modifying very large programs in this manner would be an onerous task—so onerous, in fact, that programmers might prefer instead to forego entirely the use of the new Java libraries and generic type mechanisms.
Another approach to solving the above problem involves implementing the compiler in such a way that the compiler permits programs to contain invocations of generic type declarations even if those invocations do not specify actual type arguments for the formal type parameters in the generic type declarations. A generic type invocation that does not specify actual type arguments for the formal type parameters in the generic type declaration is called a “raw type” (in contrast to a “parameterized type,” which does specify such actual type arguments). When a compiler encounters an expression that refers to a raw type, the compiler warns that the correctness of the expression cannot be guaranteed; the compiler cannot perform type checking with respect to the raw type. Nevertheless, the compiler allows compilation to proceed. The burden is placed on the programmer to ensure that the expression does not cause undesirable effects at run time.
The Java runtime environment traditionally has provided run time data structures and mechanisms that a program could use to determine, at run time, class information about objects that have been instantiated. For example, using the “instanceof” operator, a Java program could determine whether a specified object was an instance of a specified class.
However, when a Java program is compiled, the actual type arguments that are specified in parameterized types are not preserved in the resulting binary file; such arguments are lost in a process called “erasure.” Although not necessarily its sole purpose, erasure allows programs that contain raw types to interoperate with libraries that contain generic type declarations, as described above. Through erasure, the binary representation of an instance of a parameterized type becomes identical to the binary representation of an instance of the corresponding raw type. Because these binary representations are identical, old binary files, which were compiled from programs that contained raw types, can be linked with new binary files, which were compiled from libraries that contained generic type declarations.
Unfortunately, as a consequence of erasure, the Java runtime environment currently does not have any way of allowing a program to determine some specific information about objects that are instances of parameterized types.
For example, the Java runtime environment currently does not allow a program to determine whether a specified object is an instance of a parameterized type that has specified actual type arguments. Although the Java runtime environment can accurately evaluate an expression whose result depends upon whether a variable “x” is a variable of type “Collection” generally, for example, the Java runtime environment currently cannot accurately evaluate an expression whose result depends upon whether variable “x” is a variable of type “Collection<String>” specifically. Even if the source code originally expressed a parameterized type with actual type argument “<String>,” the actual type argument “<String>” would have been erased when the program was compiled into a binary file.
Nevertheless, programmers, having worked for so long with a Java runtime environment that could accurately evaluate expressions of the former kind, naturally expect that the Java runtime environment should be able to evaluate, accurately, expressions of the latter kind.